package core

import (
	"gitee.com/feimos/dm/utils"
	"gopkg.in/yaml.v3"
	"io/ioutil"
	"log"
	"os"
	"strings"
)

type YamlContent struct {
	Root  string                 `yaml:"root"`
	Nodes map[string][]*FileInfo `yaml:"content"`
}

func NewYaml(root string) *YamlContent {
	return &YamlContent{Root: root, Nodes: make(map[string][]*FileInfo)}
}

func (y *YamlContent) ExistItem(ext string, info *FileInfo) int {
	if node, ok := y.Nodes[ext]; ok {
		for index, item := range node {
			if item.Size == info.Size && item.Name == info.Name && item.Time.Equal(info.Time) {
				return index
			}
		}
		return -1
	} else {
		return -1
	}
}

func (y *YamlContent) ExistItemName(ext string, info *FileInfo) int {
	if node, ok := y.Nodes[ext]; ok {
		for index, item := range node {
			if item.Name == info.Name {
				return index
			}
		}
		return -1
	} else {
		return -1
	}
}

func (y *YamlContent) AddItem(ext string, info *FileInfo) {
	if _, ok := y.Nodes[ext]; ok {
		if -1 == y.ExistItem(ext, info) {
			y.Nodes[ext] = append(y.Nodes[ext], info)
		}

	} else {
		y.Nodes[ext] = []*FileInfo{info}
	}
}

// UpdateItem
//
// 2 add
//
// 1 update with name existed
//
// 0 update without name existed
//
// -1 not found
func (y *YamlContent) UpdateItem(ext string, info *FileInfo) int {
	if _, ok := y.Nodes[ext]; ok {
		index := y.ExistItem(ext, info)
		if -1 == index {
			subIndex := y.ExistItemName(ext, info)

			if -1 == subIndex {
				y.Nodes[ext] = append(y.Nodes[ext], info)
				return 2
			} else {
				y.Nodes[ext][subIndex] = info
				return 1
			}
		} else {
			y.Nodes[ext][index] = info
			return 0
		}
	} else {
		y.Nodes[ext] = []*FileInfo{info}
		return -1
	}
}

func (y *YamlContent) RemoveItem(ext string, info *FileInfo) bool {
	if _, ok := y.Nodes[ext]; ok {
		index := y.ExistItemName(ext, info)

		if index >= 0 {
			y.Nodes[ext] = append(y.Nodes[ext][:index], y.Nodes[ext][index+1:]...)
			return true
		}
		return false
	}

	return false
}

func (y *YamlContent) RemoveIndex(ext string, index int) {

	if 0 < index && index < len(y.Nodes[ext]) {
		y.Nodes[ext] = append(y.Nodes[ext][:index], y.Nodes[ext][index+1:]...)
	}
}

func BuildYaml(root string, fileMap FileMap) (*YamlContent, error) {

	yamlContent := NewYaml(root)

	for ext, info := range fileMap {

		dir := utils.JoinPath(root, ext)
		isCreated, err := utils.CreateDir(dir)

		if isCreated {
			log.Printf("create folder \"%v\"\n", dir)
		}

		if err != nil {
			return nil, err
		}

		yamlContent.Nodes[ext] = []*FileInfo{}

		for _, fileInfo := range info {

			newPath := utils.JoinPath(dir, fileInfo.Name)

			err := os.Rename(fileInfo.Path, newPath)

			if err != nil {
				return nil, err
			}

			yamlContent.Nodes[ext] = append(yamlContent.Nodes[ext], fileInfo)

			if strings.ToLower(fileInfo.Path) != strings.ToLower(newPath) {
				log.Printf("move \"%v\" => \"%v\"\n", fileInfo.Path, newPath)
			}

		}
	}

	return yamlContent, nil
}

func CleanYaml(yamlContent *YamlContent) {
	for ext, item := range yamlContent.Nodes {
		if len(item) == 0 {
			delete(yamlContent.Nodes, ext)
		}
	}
}

func WriteYaml(yamlContent *YamlContent, isUpdate bool) error {

	CleanYaml(yamlContent)
	content, err := yaml.Marshal(yamlContent)

	if err != nil {
		return err
	}

	dmFile := yamlContent.Root + "\\" + DmFile

	if err != nil {
		return err
	}

	err = ioutil.WriteFile(dmFile, content, 0644)

	if err != nil {
		return err
	}

	if isUpdate {
		log.Printf("update config file in \"%v\"\n", dmFile)
	} else {
		log.Printf("generate config file in \"%v\"\n", dmFile)
	}

	return nil
}

func LoadYaml(root string) (*YamlContent, error) {

	yamlContent := NewYaml(root)
	yamlFile := utils.JoinPath(root, DmFile)

	content, err := ioutil.ReadFile(yamlFile)

	if err != nil {
		return nil, err
	}

	err = yaml.Unmarshal(content, &yamlContent)
	if err != nil {
		return nil, err
	}

	return yamlContent, nil

}
